#!/usr/bin/env python
# -*- coding: utf-8 -*-
# datetime: 28/3/2024 下午10:08
# Author: name
# File: custom_assertions.py
# ide: PyCharm
# description: 断言封装工具类

import re
import time
import traceback
from typing import Union

import jmespath
from requests import Response


class Check:
    """
        测试用例断言格式
        checker = {
            "code": 200,
            "json": "@.sodar_query_id!=null",
            "re": 11.11,
            "contains": "injector_basename"
        }
    """
    code: int = 200
    json: Union[str, list] = None
    re: Union[str, list] = None
    contains: Union[str, list] = None

    def __init__(self, **entries):
        self.__dict__.update(entries)


class CustomAssertions:

    def __init__(self, actual_response: Response, check: Check):
        """
            构造方法
        :param actual_response:
        :param check:
        """
        self.response = actual_response
        self.check = check
        self.log_list = []
        self.assert_result = True
        # 测试用例断言结束后,可以获取assert_result_list和assert_result中的数据直接写入到测试报告附件中或者是日志文件中

    def __str__(self):
        return f"断言结果: {self.assert_result}\n" + "\n".join(self.log_list)

    @staticmethod
    def is_valid_jmespath(expression, assert_result_list=None):
        """
            校验jmespath表达式是否有效
        :param expression:
        :param assert_result_list:
        :return:
        """
        if assert_result_list is None:
            assert_result_list = []
        try:
            jmespath.compile(expression)
            return True
        except Exception:
            assert_result_list.append(traceback.format_exc())
            return False

    @staticmethod
    def is_valid_regex(regex_str):
        """
            校验正则表达式是否有效
        :param regex_str:
        :return:
        """
        try:
            re.compile(regex_str)
            return True
        except re.error:
            return False

    def assert_status_code(self):
        """
            状态码断言,默然断言状态码为200
        :return:
        """
        if isinstance(self.check.code, int):
            if self.response.status_code == self.check.code:
                self.log_list.append(
                    f"{time.strftime('%Y-%m-%d %H:%M:%S')}_状态码断言成功: 实际接口返回的状态码{self.response.status_code}=={self.check.code}")
            else:
                self.log_list.append(
                    f"{time.strftime('%Y-%m-%d %H:%M:%S')}_状态码断言失败: 实际接口返回的状态码{self.response.status_code}=={self.check.code}")
                self.assert_result = False
        else:
            pass

    def assert_contains(self):
        """
            包含断言,验证接口返回的数据中是否包含指定的数据
        :return:
        """
        if isinstance(self.check.contains, list):
            for text in self.check.contains:
                if str(text) in self.response.text:
                    self.log_list.append(f"{time.strftime('%Y-%m-%d %H:%M:%S')}_包含断言成功: 实际接口返回数据中包含{text}")
                else:
                    self.log_list.append(f"{time.strftime('%Y-%m-%d %H:%M:%S')}_包含断言失败: 实际接口返回数据中不包含{text}")
                    self.assert_result = False
        elif isinstance(self.check.contains, str) or isinstance(self.check.contains, int) or isinstance(
                self.check.contains, float):
            if str(self.check.contains) in self.response.text:
                self.log_list.append(
                    f"{time.strftime('%Y-%m-%d %H:%M:%S')}_包含断言成功: 实际接口返回数据中包含{self.check.contains}")
            else:
                self.log_list.append(
                    f"{time.strftime('%Y-%m-%d %H:%M:%S')}_包含断言失败: 实际接口返回数据中不包含{self.check.contains}")
                self.assert_result = False
        else:
            pass

    def assert_json(self):
        """
            json断言,断言接口返回值为json数据,通过jmespath进行json断言
        :return:
        """
        if "application/json" in response.headers.get("content-type"):
            if isinstance(self.check.json, list):
                for item in self.check.json:
                    if self.is_valid_jmespath(item, self.log_list):
                        if jmespath.search(item, self.response.json()):
                            self.log_list.append(f"{time.strftime('%Y-%m-%d %H:%M:%S')}_json断言成功: {item}")
                        else:
                            self.log_list.append(
                                f"{time.strftime('%Y-%m-%d %H:%M:%S')}_json断言失败: {item},验证为假")
                            self.assert_result = False
                    else:
                        self.log_list.append(f"{time.strftime('%Y-%m-%d %H:%M:%S')}_断言失败: {item},表达式书写有误,请检查")
                        self.assert_result = False
            elif isinstance(self.check.json, str):
                if self.is_valid_jmespath(self.check.json, self.log_list):
                    if jmespath.search(self.check.json, self.response.json()):
                        self.log_list.append(
                            f"{time.strftime('%Y-%m-%d %H:%M:%S')}_json断言成功: {self.check.json},验证结果为{jmespath.search(self.check.json, self.response.json())}")
                    else:
                        self.log_list.append(
                            f"{time.strftime('%Y-%m-%d %H:%M:%S')}_json断言失败: {self.check.json},验证为假")
                        self.assert_result = False
                else:
                    self.log_list.append(
                        f"{time.strftime('%Y-%m-%d %H:%M:%S')}_断言失败: {self.check.json},表达式书写有误,请检查")
                    self.assert_result = False
            else:
                pass

    def assert_re(self):
        """
            正则表达式断言,验证接口返回数据中是否满足正则表达式的要求
        :return:
        """
        if isinstance(self.check.re, list):
            for item in self.check.re:
                if self.is_valid_regex(item):
                    match = re.findall(item, self.response.text)
                    if match:
                        self.log_list.append(
                            f"{time.strftime('%Y-%m-%d %H:%M:%S')}_正则表达式断言成功: 表达式:{item}匹配结果为:{match}")
                    else:
                        self.log_list.append(
                            f"{time.strftime('%Y-%m-%d %H:%M:%S')}_正则表达式断言失败: 表达式:{item}匹配结果为:{match},验证为假")
                        self.assert_result = False
        elif isinstance(self.check.re, str) or isinstance(self.check.re, int) or isinstance(self.check.re, float):
            if self.is_valid_regex(str(self.check.re)):
                match = re.findall(str(self.check.re), self.response.text)
                if match:
                    self.log_list.append(
                        f"{time.strftime('%Y-%m-%d %H:%M:%S')}_正则表达式断言成功: 表达式:{self.check.re}匹配结果为:{match}")
                else:
                    self.log_list.append(
                        f"{time.strftime('%Y-%m-%d %H:%M:%S')}_正则表达式断言失败: 表达式:{self.check.re}匹配结果为:{match},验证为假")
                    self.assert_result = False
        else:
            pass


if __name__ == '__main__':
    checker = {
        "code": 200,
        "json": "@.sodar_query_id!=null",
        "re": 11.11,
        "contains": "injector_basename"
    }

    import requests

    response = requests.get("http://127.0.0.1:8081/variable/page")
    print(response)
    ca = CustomAssertions(response, Check(**checker))
    ca.assert_status_code()
    ca.assert_contains()
    ca.assert_json()
    ca.assert_re()
    print(ca.assert_result)
    print(ca.assert_result_list)
    print(ca)
